/**
 * @file 	 lnovance_md.c
 * @brief       
 * @author 	 wangweihu
 * @version  1.0
 * @date 	 2023-12-29
 * 
 * @copyright Copyright (c) 2023  YGL
 * 
 */
#include "lnovance_md.h"
#include "stdlib.h"
#include "string.h"
#include "aos/kv.h"
#include "boardio.h"

#define ABS(n)  (n < 0 ? (-n) : (n))
#define IS_VALID_PIN(id)    (id > 0 ? (true) : (false))

//不同的项目需要在应用层配置好Type_Lnovance_Def列表然后传入初始化
/**************************************************************/
uint8_t g_listLen = 0;
Type_Lnovance_Def *g_lnovance_list = NULL;
/**************************************************************/

static int xp_lnovance_debug(char *type,char *fun,char *param);

bool _xp_lnovance_product_number(Type_Lnovance_Def * const handle);
bool _xp_lnovance_update_current(Type_Lnovance_Def * const handle);
bool _xp_lnovance_update_state(Type_Lnovance_Def * const handle);
bool _xp_lnovance_update_error_code(Type_Lnovance_Def * const handle);

int xp_lnovance_write_reginster(Type_Lnovance_Def * const handler, uint16_t addr, uint16_t *data, uint8_t len);
int xp_lnovance_read_reginster(Type_Lnovance_Def * const handler, uint16_t addr, uint16_t read_len, uint8_t *rec_data, uint8_t *rec_len);

void memcpy_resv_word(void *des, void * const res, uint16_t size);
void xp_lnovance_show(void);

// Type_Lnovance_Def lnovance[] = {
//     [0] = {
//         .com = {
//             .port = 1,
//             .slave_id = 1,
//             .speed = 9600,
//         },
//         .DI1.boardId = 0,  .DI1.pinId = 0,
//     },
// };


bool xp_lnovance_init(Type_Lnovance_Def * const pList, uint8_t listLen)
{
	int ret = 0;

    if (NULL != pList && 0 != listLen){

        g_lnovance_list = pList;
        g_listLen = listLen;

        for (uint8_t i = 0 ; i < g_listLen; i++){
            //初始化端口modbus,理论上所有变频器都是挂在同一个总线上的,端口只初始化一次就行,但为了最大化兼容,这里做支持不同端口
            ret = xp_modbus_init(g_lnovance_list[i].com.port, g_lnovance_list[i].com.speed, MB_PAR_EVEN, 100);
            
            if(0 != ret){
                println("Lnovance <id:%d> modbus init fail ! ret = %d", g_lnovance_list[i].com.slave_id, ret);
                return false;
            }else if ( _xp_lnovance_product_number(&g_lnovance_list[i]) ){   //验证通信
                g_lnovance_list[i].isInitOk = true;
                println("Lnovance <id:%d> init success, mode: %X", g_lnovance_list[i].com.slave_id, g_lnovance_list[i].mode);
            }else{
                println("Lnovance <id:%d> modbus init success, but read data failed !", g_lnovance_list[i].com.slave_id);
            }
        }

        xp_cli_debug_add(xp_lnovance_debug);
        return true;

    }else{
        return false;
    }
}

bool xp_lnovance_Deinit(void)
{
    for (int i = 0; i < g_listLen; i++){
        xp_lnovance_run(i, 0);
    }
    return true;
}

bool _xp_lnovance_product_number(Type_Lnovance_Def * const handle)
{
    int ret = xp_lnovance_read_reginster(handle, ADDR_PRODUCT_NUMBER, 1, handle->com.rec_data, &handle->com.rec_len);
	if (0 == ret){
		handle->mode = (handle->com.rec_data[0] << 8) | handle->com.rec_data[1];
        return true;
	}
    return false;
}

bool _xp_lnovance_update_current(Type_Lnovance_Def * const handle)
{
    int ret = xp_lnovance_read_reginster(handle, ADDR_CURRENT, 1, handle->com.rec_data, &handle->com.rec_len);
	if (0 == ret){
		handle->param.current = (handle->com.rec_data[0] << 8) | handle->com.rec_data[1];
        return true;
	}
    return false;
}

bool _xp_lnovance_update_state(Type_Lnovance_Def * const handle)
{
    int ret = xp_lnovance_read_reginster(handle, ADDR_STATE, 1, handle->com.rec_data, &handle->com.rec_len);
	if (0 == ret){
		handle->param.state.u16Info = (handle->com.rec_data[0] << 8) | handle->com.rec_data[1];
        return true;
	}
    return false;
}

bool _xp_lnovance_update_error_code(Type_Lnovance_Def * const handle)
{
    int ret = xp_lnovance_read_reginster(handle, ADDR_ERROR_CODE, 2, handle->com.rec_data, &handle->com.rec_len);
	if (0 == ret){
		memcpy_resv_word(&handle->param.errorCode, handle->com.rec_data, handle->com.rec_len);
        return true;
	}
    return false;
}

bool _xp_lnovance_clear_error(Type_Lnovance_Def * const handle)
{
    // uint16_t date = 0x9696;     // 重置变频器（厂家推荐的故障清除方法）
    // //uint16_t date = 0xA5A5;   // 清除故障信息
    // int ret = xp_lnovance_write_reginster(handle, ADDR_CLEAR_ERROR, &date, 1);
	// if (0 == ret){
    //     return true;
	// }
    return false;
}

bool _xp_lnovance_set_stage_speed(Type_Lnovance_Def * const handle, uint16_t speed_1, uint16_t speed_5, uint16_t speed_6, uint16_t speed_7)
{
    // int ret = -1;
    // uint16_t tmp1[2] = {speed_1, 3000};
    // ret = xp_lnovance_write_reginster(handle, ADDR_STAGE_SPEED_1, &tmp1, 2);
	// if (0 != ret){
    //     return false;
	// }
    // uint16_t tmp2[3] = {speed_5, speed_6, speed_7};
    // ret = xp_lnovance_write_reginster(handle, ADDR_STAGE_SPEED_5, &tmp2, 3);
	// if (0 != ret){
        return false;
	// }
    // return true;
}

int xp_lnovance_write_reginster(Type_Lnovance_Def * const handler, uint16_t addr, uint16_t *data, uint8_t len)
{
    int sta = 0;
    MB_data modbus;
    uint8_t try_times = 3;

    modbus.slave_id = handler->com.slave_id;
    modbus.mb_fun = mb_hold_w;
    modbus.start_addr = addr;
    modbus.wBuf = data;
    modbus.rwLen = len;
    modbus.resBuf = handler->com.rec_data;
    modbus.resLen = &handler->com.rec_len;
    while(try_times--){
        if( 0 == xp_modbus_ask(handler->com.port, &modbus) ) return 0;
    }

    return -1;
}

int xp_lnovance_read_reginster(Type_Lnovance_Def * const handler, uint16_t addr, uint16_t read_len, uint8_t *rec_data, uint8_t *rec_len)
{
    int sta = 0;
    MB_data modbus;
    uint8_t try_times = 3;

    modbus.slave_id = handler->com.slave_id;
    modbus.mb_fun = mb_hold_r;
    modbus.start_addr = addr;
    modbus.rwLen = read_len;
    modbus.resBuf = rec_data;
    modbus.resLen = rec_len;
    while(try_times--){
        if( 0 == xp_modbus_ask(handler->com.port, &modbus) ) return 0;
    }
	println("lnovance read id %d fail !!!", modbus.slave_id);
    return -1;
}
//带大小端转换的memcpy  __REVSH
static void memcpy_resv_word(void *des, void * const res, uint16_t size)
{
	for (uint16_t i = 0; i < size; i += sizeof(uint32_t)){
		*((uint8_t *)des + i + 0) = *((uint8_t *)res + i + 3);
		*((uint8_t *)des + i + 1) = *((uint8_t *)res + i + 2);
		*((uint8_t *)des + i + 2) = *((uint8_t *)res + i + 1);
		*((uint8_t *)des + i + 3) = *((uint8_t *)res + i + 0);
	}
}
//输出各参数
void xp_lnovance_show(void)
{
    for (uint8_t i = 0 ; i < g_listLen; i++){
        println("id: <%d> slave id: <%d>  Current: %d mA  error code: 0x%04X\r\n", i, g_lnovance_list[i].com.slave_id, g_lnovance_list[i].param.current, g_lnovance_list[i].param.errorCode);
    }
}

/**
 * @description: 调试接口
 * @param {char} *type
 * @param {char} *fun
 * @param {char} *param
 * @return {int}            :匹配成功返回1
 */
static int xp_lnovance_debug(char *type, char *fun, char *param)
{
	int ret = -1;
	
	//本调试接口仅支持 "lnovance_" 起始的命令
	if (NULL == strstr(type, "lnovance_")){
		return 0;
	}else{
		type += strlen("lnovance_");
	}
    //xp lnovance_show  打印所测量信息
	if (strcasecmp(type, "show") == 0){
		xp_lnovance_show();
	}
    else if (strcasecmp(type, "move") == 0){
		println("Move %s\r\n", xp_lnovance_run(atoi(fun), atoi(param)) == true ? ("success") : ("failed"));
	}
    else if (strcasecmp(type, "stage_test") == 0){
		println("stage_test %s\r\n", xp_lnovance_set_stage_speed(atoi(fun), atoi(param), 4000,4100,4200) == true ? ("success") : ("failed"));
	}
    else {
        return 0;
    }
    return 1;
}


//外部接口
/********************************************************************************************/
void xp_lnovance_write_pin(const uint8_t board_id,const uint8_t pin,const bool isEnable)
{
    if (IS_VALID_PIN(board_id)){    //通过板ID是否为0验证此IO是否实际使用(板ID从1开始)
        xp_io_write_pin(board_id, pin, isEnable ? (0) : (1));
    }
}

void xp_lnovance_write_pins(const uint8_t board_id,const uint32_t states,const uint8_t start_pin, const uint8_t pins)
{
    if (IS_VALID_PIN(board_id)){    //通过板ID是否为0验证此IO是否实际使用(板ID从1开始)
        xp_io_write_pins(board_id, states, start_pin, pins);
    }
}

bool xp_lnovance_get_current(int devId, int16_t *pResult)
{
    if (devId >= g_listLen || NULL == pResult || NULL == g_lnovance_list){
        return false;
    }else{
        if (_xp_lnovance_update_current(&g_lnovance_list[devId])){
            *pResult = g_lnovance_list[devId].param.current;
            return true;
        }else{
            return false;
        }
    }
}

bool xp_lnovance_get_error_code(int devId, uint32_t *pResult)
{
    if (devId >= g_listLen || NULL == pResult || NULL == g_lnovance_list){
        return false;
    }else{
        if (_xp_lnovance_update_error_code(&g_lnovance_list[devId])){
            *pResult = g_lnovance_list[devId].param.errorCode;
            return true;
        }else{
            return false;
        }
    }
}

bool xp_lnovance_get_state(int devId, uint16_t *pResult)
{
    if (devId >= g_listLen || NULL == pResult || NULL == g_lnovance_list){
        return false;
    }else{
        if (_xp_lnovance_update_state(&g_lnovance_list[devId])){
            *pResult = g_lnovance_list[devId].param.state.u16Info;
            return true;
        }else{
            return false;
        }
    }
}
bool xp_lnovance_set_stage_speed(int devId, uint16_t speed_1, uint16_t speed_5, uint16_t speed_6, uint16_t speed_7)
{
    if (devId >= g_listLen || NULL == g_lnovance_list){
        return false;
    }else{
        return _xp_lnovance_set_stage_speed(&g_lnovance_list[devId], speed_1, speed_5, speed_6, speed_7);
    }
}
bool xp_lnovance_clear_error(int devId)
{
    if (devId >= g_listLen || NULL == g_lnovance_list){
        return false;
    }else{
        return _xp_lnovance_clear_error(&g_lnovance_list[devId]);
    }
}

bool xp_lnovance_run(uint8_t devId, int speed)
{
    if (devId >= g_listLen || NULL == g_lnovance_list){
        println("dev id error\r\n");
        return false;
    }

    // println("xp_lnovance_run: devId %d speed %d", devId, speed);
    if (speed > 0){         //正转, MCU输出低电平导通MOS使得外接的驱动器高电平信号短接到公共端从而作用
        xp_lnovance_write_pin(g_lnovance_list[devId].DI1.boardId, g_lnovance_list[devId].DI1.pinId, true);
        xp_lnovance_write_pin(g_lnovance_list[devId].DI2.boardId, g_lnovance_list[devId].DI2.pinId, false);
    }else if (speed < 0){   //反转
        xp_lnovance_write_pin(g_lnovance_list[devId].DI1.boardId, g_lnovance_list[devId].DI1.pinId, false);
        xp_lnovance_write_pin(g_lnovance_list[devId].DI2.boardId, g_lnovance_list[devId].DI2.pinId, true);
    }else{                  //停止 speed == 0
        xp_lnovance_write_pin(g_lnovance_list[devId].DI1.boardId, g_lnovance_list[devId].DI1.pinId, false);
        xp_lnovance_write_pin(g_lnovance_list[devId].DI2.boardId, g_lnovance_list[devId].DI2.pinId, false);
    }

	switch( ABS((int)speed) ){

		case 1:
			// xp_lnovance_write_pin(g_lnovance_list[devId].gpio_M0.boardId, g_lnovance_list[devId].gpio_M0.pinId, false);
			// xp_lnovance_write_pin(g_lnovance_list[devId].gpio_M1.boardId, g_lnovance_list[devId].gpio_M1.pinId, false);
		break;
		
		case 2:
			// xp_lnovance_write_pin(g_lnovance_list[devId].gpio_M0.boardId, g_lnovance_list[devId].gpio_M0.pinId, true);
			// xp_lnovance_write_pin(g_lnovance_list[devId].gpio_M1.boardId, g_lnovance_list[devId].gpio_M1.pinId, false);
		break;
		
		case 3:
			// xp_lnovance_write_pin(g_lnovance_list[devId].gpio_M0.boardId, g_lnovance_list[devId].gpio_M0.pinId, false);
			// xp_lnovance_write_pin(g_lnovance_list[devId].gpio_M1.boardId, g_lnovance_list[devId].gpio_M1.pinId, true);
		break;
		
		case 4:
			// xp_lnovance_write_pin(g_lnovance_list[devId].gpio_M0.boardId, g_lnovance_list[devId].gpio_M0.pinId, true);
			// xp_lnovance_write_pin(g_lnovance_list[devId].gpio_M1.boardId, g_lnovance_list[devId].gpio_M1.pinId, true);
		break;
		
		default:
			// xp_lnovance_write_pin(g_lnovance_list[devId].DI1.boardId, g_lnovance_list[devId].DI1.pinId, false);
			// xp_lnovance_write_pin(g_lnovance_list[devId].DI2.boardId, g_lnovance_list[devId].DI2.pinId, false);
			// xp_lnovance_write_pin(g_lnovance_list[devId].gpio_M0.boardId, g_lnovance_list[devId].gpio_M0.pinId, false);
			// xp_lnovance_write_pin(g_lnovance_list[devId].gpio_M1.boardId, g_lnovance_list[devId].gpio_M1.pinId, false);
		break;
	}
	return true;
}
